import struct
import ctypes
import pylynchipsdk as sdk
from common.python.infer_process import *
from common.python.callback_data_struct import *
from ctypes import *
import ipe_param_face_recongnition

pythonapi.PyCapsule_GetPointer.restype = c_void_p
pythonapi.PyCapsule_GetPointer.argtypes = [py_object, c_char_p]


class FaceDetectBox(ctypes.Structure):
    _fields_ = [
        ("xmin", ctypes.c_float),
        ("ymin", ctypes.c_float),
        ("xmax", ctypes.c_float),
        ("ymax", ctypes.c_float),
        ("area", ctypes.c_float),
        ("score", ctypes.c_float),
        ("id", ctypes.c_int),
        ("landmark", ctypes.ARRAY(ctypes.c_float, 10)),
    ]


class FaceDetectInfo(ctypes.Structure):
    _fields_ = [
        ("boxesNum", ctypes.c_uint16),
        ("boxes", ctypes.ARRAY(FaceDetectBox, 256)),
    ]


class bbox(ctypes.Structure):
    _fields_ = [
        ("xmin", ctypes.c_uint32),
        ("ymin", ctypes.c_uint32),
        ("xmax", ctypes.c_uint32),
        ("ymax", ctypes.c_uint32),
        ("score", ctypes.c_float),
        ("id", ctypes.c_int),
        ("label", ctypes.c_char * 64),
    ]


class Box(ctypes.Structure):
    _fields_ = [("boxesnum", ctypes.c_uint32), ("boxes", ctypes.ARRAY(bbox, 256))]


def dump_box_json(output_path, c_box):
    box_dict = {"boxesnum": c_box.boxesnum, "boxes": []}
    for i in range(c_box.boxesnum):
        bbox_instance = c_box.boxes[i]
        bbox_dict = {
            "xmin": bbox_instance.xmin,
            "ymin": bbox_instance.ymin,
            "xmax": bbox_instance.xmax,
            "ymax": bbox_instance.ymax,
            "score": bbox_instance.score,
            "label": bbox_instance.label.decode("utf-8"),
        }
        box_dict["boxes"].append(bbox_dict)

    # Serialize the dictionary to JSON
    with open(output_path, "a", encoding="utf-8") as f:
        f.seek(0, os.SEEK_END)
        file_size = f.tell()
        if file_size < 500 * 1024 * 1024:
            json.dump(box_dict, f, indent=2, ensure_ascii=False)
            f.write("\n")
        else:
            print("only support 500M.", output_path, "now file size:", file_size)


def cal_event_cost(args):
    time, ret = sdk.lyn_event_elapsed_time(args[0], args[1])
    common.error_check(ret, "lyn_event_elapsed_time")
    if args[2]:
        args[2][0] += 1
        args[2][1] += time
    return 0


def lyn_free_callback(args):
    for i in args:
        common.error_check(sdk.lyn_free(i))
    return 0


def ipe_destory_callback(args):
    for i in args:
        i.destory()
    return 0


def return_buffer(callback_data) -> int:
    """将buffer还回buffer pool中

    Args:
        callback_data (list): 大小为2的list：[buffer pool, buffer]

    Returns:
        _type_: integer，默认为0
    """
    callback_data[0].push(callback_data[1])
    return 0


def free_device_memory(mem) -> int:
    """释放lyn_malloc申请的内存

    Args:
        mem (): device侧的内存地址

    Returns:
        _type_: integer，默认为0
    """
    sdk.lyn_free(mem)
    return 0


def return_buffers(callback_data) -> int:
    """将列表中的buffer还回buffer pool中

    Args:
        callback_data (list): 大小为2的list：[buffer pool, buffers]，第二个元素为buffer组成的list

    Returns:
        _type_: integer，默认为0
    """
    for buffer in callback_data[1]:
        callback_data[0].push(buffer)
    return 0


def put_to_queue(callback_data) -> int:
    """将元素放入queue中

    Args:
        callback_data (list): 大小为2的list：[queue, element]

    Returns:
        _type_: integer，默认为0
    """
    callback_data[0].put(callback_data[1])
    return 0


class Encoder:
    """视频编码类"""

    def __init__(self, ctx, framepool, vdec_out_info, output_path):
        self.frame_pool = framepool
        sdk.lyn_set_current_context(ctx)
        self.venc_recv_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        self.venc_send_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        self.enc_head_flag = True
        venc_attr = sdk.lyn_venc_attr_t()
        ret = sdk.lyn_venc_set_default_params(venc_attr)
        common.error_check(ret, "lyn_venc_set_default_params")
        venc_attr.codec_type = sdk.lyn_codec_id_t.LYN_CODEC_ID_H264
        self.vdec_out_info = vdec_out_info
        venc_attr.width = self.vdec_out_info.width
        venc_attr.height = self.vdec_out_info.height
        venc_attr.bit_depth = 8
        venc_attr.bframes_num = 0
        venc_attr.pframes_num = 5
        venc_attr.input_format = sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12
        venc_attr.target_bitrate = 6000000
        venc_attr.level = -1
        self.venc_handle, ret = sdk.lyn_venc_open(venc_attr)
        common.error_check(ret, "lyn_venc_open")
        self.output_path = output_path
        self.venc_recv_pool = bufferpool.buffer_pool(
            self.vdec_out_info.predict_buf_size, 5
        )

    def encode(
        self,
        frame,
    ):
        if self.enc_head_flag:
            self.enc_head_flag = False
            enc_packet = sdk.lyn_packet_t()
            enc_packet.size = self.vdec_out_info.predict_buf_size
            enc_packet.data = self.venc_recv_pool.pop()
            encode_data = save_file_cb_data()
            encode_data.output_path = self.output_path
            encode_data.packet = enc_packet
            encode_data.recv_pool = self.venc_recv_pool
            encode_data.file_path = self.output_path
            ret = sdk.lyn_venc_get_paramsset_async(
                self.venc_recv_stream, self.venc_handle, enc_packet
            )
            common.error_check(ret, "lyn_venc_get_paramsset_async")
            ret = sdk.lyn_stream_add_callback(
                self.venc_recv_stream, save_file_cb, encode_data
            )
            common.error_check(ret, "lyn_stream_add_callback")

        ret = sdk.lyn_venc_sendframe_async(
            self.venc_send_stream, self.venc_handle, frame
        )
        common.error_check(ret, "lyn_venc_sendframe_async")
        enc_packet = sdk.lyn_packet_t()
        enc_packet.size = self.vdec_out_info.predict_buf_size
        enc_packet.eos = frame.eos
        enc_packet.data = self.venc_recv_pool.pop()
        encode_data = save_file_cb_data()
        encode_data.packet = enc_packet
        encode_data.recv_pool = self.venc_recv_pool
        encode_data.file_path = self.output_path
        encode_data.output_path = self.output_path
        ret = sdk.lyn_stream_add_callback(
            self.venc_send_stream,
            free_to_pool_callback,
            [self.frame_pool, frame.data],
        )
        common.error_check(ret, "lyn_stream_add_callback")
        ret = sdk.lyn_venc_recvpacket_async(
            self.venc_recv_stream, self.venc_handle, enc_packet
        )
        common.error_check(ret, "lyn_venc_recvpacket_async")
        ret = sdk.lyn_stream_add_callback(
            self.venc_recv_stream, save_file_cb, encode_data
        )
        common.error_check(ret, "lyn_stream_add_callback")

    def __del__(self):
        sdk.lyn_synchronize_stream(self.venc_recv_stream)
        sdk.lyn_synchronize_stream(self.venc_send_stream)
        sdk.lyn_destroy_stream(self.venc_recv_stream)
        sdk.lyn_destroy_stream(self.venc_send_stream)
        sdk.lyn_venc_close(self.venc_handle)


class OpencvWindow:
    """opencv窗口类"""

    def __init__(self, frame_pool, video_frame, vdec_out_info):
        self.frame_pool = frame_pool
        self.video_frame = video_frame
        self.vdec_out_info = vdec_out_info

    def show(self, frame):
        data = np.empty(frame.size, dtype=np.uint8)
        data_ptr = sdk.lyn_numpy_to_ptr(data)
        ret = sdk.lyn_memcpy(
            data_ptr,
            frame.data,
            frame.size,
            sdk.lyn_memcpy_dir_t.ServerToClient,
        )
        common.error_check(ret, "lyn_memcpy")
        yuvImg = np.reshape(
            data, (self.vdec_out_info.height * 3 // 2, self.vdec_out_info.width)
        ).astype(np.uint8)
        if frame.eos:
            self.video_frame.put([yuvImg, frame.eos])
        else:
            self.video_frame.queue.clear()
            self.video_frame.put([yuvImg, frame.eos])
        self.frame_pool.push(frame.data)
        return 0


class FaceDetection:
    """人脸检测类"""

    def __init__(self):
        self.device_id = 0
        self.channel_id = 0
        self.model_path = ""
        self.show_type = 1
        self.video_frame = ""
        self.input_file = ""
        self.output_path = ""
        self.post_plugin_path = ""
        self.osd_plugin_path = ""
        self.detect_model_path = ""
        self.print_stats = 0
        self.cost_info = {}

    def init(self):
        self.__ctx, ret = sdk.lyn_create_context(self.device_id)
        self.use_local_input = os.path.exists(self.input_file)

        common.error_check(ret, "lyn_create_context")

        if self.print_stats > 0:
            self.cost_info = {
                "face_detect_preprocess": [0, 0],
                "face_detect_infer": [0, 0],
                "face_detect_postprocess": [0, 0],
            }
        self.init_demuxer()
        self.init_decoder()
        self.create_queue()
        self.register_plugin()

    def init_decoder(self):
        sdk.lyn_set_current_context(self.__ctx)
        self.__vdec_attr = sdk.lyn_vdec_attr_t()
        self.__vdec_attr.codec_id = self.codec_para.codec_id
        self.__vdec_attr.output_fmt = sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12
        self.__vdec_attr.scale = sdk.lyn_scale_t.SCALE_NONE
        self.__vdec_hdl, ret = sdk.lyn_vdec_open(self.__vdec_attr)
        common.error_check(ret, "lyn_vdec_open")
        self.vdec_out_info, ret = sdk.lyn_vdec_get_out_info(
            self.codec_para, self.__vdec_attr
        )
        common.error_check(ret, "lyn_vdec_get_out_info")
        self.frame_pool = bufferpool.buffer_pool(self.vdec_out_info.predict_buf_size, 5)

    def init_demuxer(self):
        self.__demux_hdl, ret = sdk.lyn_demux_open(self.input_file)
        common.error_check(ret, "lyn_demux_open")
        self.codec_para, ret = sdk.lyn_demux_get_codec_para(self.__demux_hdl)
        common.error_check(ret, "lyn_demux_get_codec_para")

    def create_queue(self):
        self.__send_queue = block_queue()
        self.__recv_queue = block_queue()
        self.__detect_result_queue = block_queue()

    def register_plugin(self):
        self.post_plugin, ret = sdk.lyn_plugin_register(self.post_plugin_path)
        common.error_check(ret, "lyn_plugin_register")
        self.osd_plugin, ret = sdk.lyn_plugin_register(self.osd_plugin_path)
        common.error_check(ret, "lyn_plugin_register")

    def run(self, cancel_flag):
        self.__decode_send_thread = threading.Thread(
            target=self.decoder_send, args=(cancel_flag,)
        )
        self.__decode_send_thread.start()

        # 开启接收线程
        self.__decode_recv_thread = threading.Thread(
            target=self.decoder_recv, args=(cancel_flag,)
        )
        self.__decode_recv_thread.start()

        # 开启检测线程
        self.__detect_thread = threading.Thread(
            target=self.face_detect, args=(cancel_flag,)
        )
        self.__detect_thread.start()

        # 开启展示或保存线程
        self.__generate_result_thread = threading.Thread(
            target=self.generate_result, args=(cancel_flag,)
        )
        self.__generate_result_thread.start()

    def decoder_send(self, cancel_flag):
        # 设置上下文环境 创建发送stream
        sdk.lyn_set_current_context(self.__ctx)
        send_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        eos, reconnect = False, False
        while not eos:
            # 从解封装器读取一个包
            pkt, ret = sdk.lyn_demux_read_packet(self.__demux_hdl)
            # common.error_check(ret, "lyn_demux_read_packet")
            eos = pkt.eos
            reconnect = ret == 101003
            if reconnect:
                eos = False
                self.__send_queue.put((eos, reconnect))
                ret = sdk.lyn_vdec_send_packet_async(send_stream, self.__vdec_hdl, pkt)
                self.__decode_recv_thread.join()
                self.__generate_result_thread.join()
                self.frame_pool.free_buffers()
                sdk.lyn_vdec_close(self.__vdec_hdl)
                sdk.lyn_demux_close(self.__demux_hdl)

                self.init_demuxer()
                self.init_decoder()
                print(
                    f"cnannel {self.device_id}_{self.channel_id} changed resolution to: {self.vdec_out_info.width}, {self.vdec_out_info.height}"
                )

                self.__decode_recv_thread = threading.Thread(
                    target=self.decoder_recv, args=(cancel_flag,)
                )
                self.__decode_recv_thread.start()
                self.__generate_result_thread = threading.Thread(
                    target=self.generate_result, args=(cancel_flag,)
                )
                self.__generate_result_thread.start()
                continue

            if (eos or ret == lynEEOF) and not self.use_local_input:
                sdk.lyn_demux_close(self.__demux_hdl)
                time.sleep(500.0 / 1000)
                print("demux failed, reconnecting...")
                self.__demux_hdl, ret = sdk.lyn_demux_open(self.input_file)
                common.error_check(ret, "lyn_demux_open")
                eos = False
                continue

            # 发送给解码器解码
            ret = sdk.lyn_vdec_send_packet_async(send_stream, self.__vdec_hdl, pkt)
            common.error_check(ret, "lyn_vdec_send_packet_async")
            ret = sdk.lyn_synchronize_stream(send_stream)
            common.error_check(ret, "lyn_synchronize_stream")
            # 释放packet内存并通知接收结果
            if not eos:
                sdk.lyn_demux_free_packet(pkt)
            self.__send_queue.put((eos, reconnect))
            if cancel_flag.value:
                return

    def decoder_recv(self, cancel_flag):
        sdk.lyn_set_current_context(self.__ctx)
        recv_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        eos, reconnect = False, False
        while not eos:
            eos, reconnect = self.__send_queue.take()
            if reconnect:
                cb_data = recv_cb_data()
                cb_data.reconnect = reconnect
                ret = sdk.lyn_stream_add_callback(
                    recv_stream, put_to_queue, [self.__recv_queue, cb_data]
                )
                self.__send_queue.clear()
                break
            cb_data = recv_cb_data()
            cb_data.frame.eos = eos
            cb_data.frame.data = self.frame_pool.pop()
            cb_data.frame.size = self.vdec_out_info.predict_buf_size
            cb_data.frame_pool = self.frame_pool
            cb_data.block_queue = self.__recv_queue
            cb_data.video_frame = self.video_frame
            ret = sdk.lyn_vdec_recv_frame_async(
                recv_stream, self.__vdec_hdl, cb_data.frame
            )
            common.error_check(ret, "lyn_vdec_recv_frame_async")
            ret = sdk.lyn_stream_add_callback(
                recv_stream, put_to_queue, [self.__recv_queue, cb_data]
            )
            common.error_check(ret, "lyn_stream_add_callback")
            if cancel_flag.value:
                return

    def detect_ipe(self, ipe_stream, frame, ipe_out_data, model_width, model_height):
        pass

    def face_detect(self, cancel_flag):
        sdk.lyn_set_current_context(self.__ctx)
        ipe_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        apu_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        post_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        osd_stream, ret = sdk.lyn_create_stream()
        common.error_check(ret, "lyn_create_stream")
        ipe_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        apu_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        post_event, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        face_detect_model, ret = sdk.lyn_load_model(self.detect_model_path)
        common.error_check(ret, "lyn_load_model")

        ipe_event_begin, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        ipe_event_end, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        apu_event_begin, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        apu_event_end, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        post_event_begin, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")
        post_event_end, ret = sdk.lyn_create_event()
        common.error_check(ret, "lyn_create_event")

        # 获取模型信息
        model_desc, ret = sdk.lyn_model_get_desc(face_detect_model)
        common.error_check(ret, "lyn_model_get_desc")
        batch_size = model_desc.inputTensorAttrArray[0].batchSize
        model_width = model_desc.inputTensorAttrArray[0].dims[2]
        model_height = model_desc.inputTensorAttrArray[0].dims[1]
        ipe_output_size = (
            model_width * model_height * model_desc.inputTensorAttrArray[0].dims[3]
        )
        apu_output_size = model_desc.outputDataLen
        apu_buffer_pool = bufferpool.buffer_pool(apu_output_size * batch_size, 5)
        ipe_buffer_pool = bufferpool.buffer_pool(ipe_output_size * batch_size, 5)
        # 初始化IPE
        ipeScrfd = ipe_param_face_recongnition.IpeParamScrfd(model_width, model_height)
        ipeScrfd.set_img_info(
            self.vdec_out_info.width,
            self.vdec_out_info.height,
            sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12,
        )

        # pDevDetectInfo, ret = sdk.lyn_malloc(ctypes.sizeof(FaceDetectInfo))
        # common.error_check(ret)
        # pDevDetectInfo_ptr = pythonapi.PyCapsule_GetPointer(pDevDetectInfo, None)

        eos = False
        while not eos:
            cb_data = self.__recv_queue.take()
            if cb_data.reconnect:
                sdk.lyn_stream_add_callback(
                    osd_stream, put_to_queue, [self.__detect_result_queue, cb_data]
                )
                self.__recv_queue.clear()
                continue

            eos = cb_data.frame.eos
            ipe_buffer = ipe_buffer_pool.pop()
            apu_buffer = apu_buffer_pool.pop()

            sdk.lyn_record_event(ipe_stream, ipe_event_begin)
            ipeScrfd.calc_param(ipe_stream, cb_data.frame.data, ipe_buffer)
            sdk.lyn_record_event(ipe_stream, ipe_event_end)
            sdk.lyn_stream_add_callback(
                ipe_stream,
                cal_event_cost,
                [
                    ipe_event_begin,
                    ipe_event_end,
                    self.cost_info.get("face_detect_preprocess", []),
                ],
            )
            # self.detect_ipe(
            #     ipe_stream, cb_data.frame, ipe_buffer, model_width, model_height
            # )
            sdk.lyn_record_event(ipe_stream, ipe_event)
            sdk.lyn_stream_wait_event(apu_stream, ipe_event)

            sdk.lyn_record_event(apu_stream, apu_event_begin)
            sdk.lyn_execute_model_async(
                apu_stream, face_detect_model, ipe_buffer, apu_buffer, batch_size
            )
            sdk.lyn_record_event(apu_stream, apu_event_end)
            sdk.lyn_stream_add_callback(
                apu_stream,
                cal_event_cost,
                [
                    apu_event_begin,
                    apu_event_end,
                    self.cost_info.get("face_detect_infer", []),
                ],
            )

            sdk.lyn_record_event(apu_stream, apu_event)
            sdk.lyn_stream_wait_event(post_stream, apu_event)

            pDevDetectApuBuf_ptr = pythonapi.PyCapsule_GetPointer(apu_buffer, None)

            pDevDetectInfo, ret = sdk.lyn_malloc(ctypes.sizeof(FaceDetectInfo))
            common.error_check(ret)
            pDevDetectInfo_ptr = pythonapi.PyCapsule_GetPointer(pDevDetectInfo, None)

            post_info = struct.pack(
                "4i2f2P",
                model_height,
                model_width,
                self.vdec_out_info.height,
                self.vdec_out_info.width,
                0.25,
                0.45,
                pDevDetectApuBuf_ptr,
                pDevDetectInfo_ptr,
            )

            sdk.lyn_record_event(post_stream, post_event_begin)
            common.error_check(
                sdk.lyn_plugin_run_async(
                    post_stream,
                    self.post_plugin,
                    "lynFacePostProcess",
                    post_info,
                    len(post_info),
                )
            )
            common.error_check(ret, "lyn_plugin_run_async")
            sdk.lyn_record_event(post_stream, post_event_end)
            sdk.lyn_stream_add_callback(
                post_stream,
                cal_event_cost,
                [
                    post_event_begin,
                    post_event_end,
                    self.cost_info.get("face_detect_postprocess", []),
                ],
            )

            sdk.lyn_stream_add_callback(
                post_stream, return_buffer, [ipe_buffer_pool, ipe_buffer, "ipe_buffer"]
            )
            sdk.lyn_stream_add_callback(
                post_stream, return_buffer, [apu_buffer_pool, apu_buffer, "apu_buffer"]
            )

            face_detect_size = ctypes.sizeof(FaceDetectInfo)
            pHostDetectInfo_np = np.zeros(face_detect_size, dtype=np.uint8)
            pHostDetectInfo = sdk.lyn_numpy_to_ptr(pHostDetectInfo_np)
            common.error_check(
                sdk.lyn_memcpy_async(
                    post_stream,
                    pHostDetectInfo,
                    pDevDetectInfo,
                    face_detect_size,
                    sdk.lyn_memcpy_dir_t.ServerToClient,
                )
            )
            common.error_check(sdk.lyn_synchronize_stream(post_stream))
            pHostDetectInfo_c = pythonapi.PyCapsule_GetPointer(pHostDetectInfo, None)
            c_face_info = ctypes.cast(
                pHostDetectInfo_c, ctypes.POINTER(FaceDetectInfo)
            ).contents

            pHostBoxesInfo = Box()
            if c_face_info.boxesNum > 0:
                pHostBoxesInfo.boxesnum = c_face_info.boxesNum

                for faceIndex in range(c_face_info.boxesNum):
                    box = c_face_info.boxes[faceIndex]
                    pHostBoxesInfo.boxes[faceIndex].xmin = int(box.xmin)
                    pHostBoxesInfo.boxes[faceIndex].ymin = int(box.ymin)
                    pHostBoxesInfo.boxes[faceIndex].xmax = int(box.xmax)
                    pHostBoxesInfo.boxes[faceIndex].ymax = int(box.ymax)
                    pHostBoxesInfo.boxes[faceIndex].score = box.score
                    pHostBoxesInfo.boxes[faceIndex].id = box.id

                    if pHostBoxesInfo.boxes[faceIndex].xmax >= self.vdec_out_info.width:
                        pHostBoxesInfo.boxes[faceIndex].xmax = (
                            self.vdec_out_info.width - 1
                        )
                    if (
                        pHostBoxesInfo.boxes[faceIndex].ymax
                        >= self.vdec_out_info.height
                    ):
                        pHostBoxesInfo.boxes[faceIndex].ymax = (
                            self.vdec_out_info.height - 1
                        )

            else:
                # print("no face detected")
                pHostBoxesInfo.boxesnum = 0
            cb_data.host_box_info = pHostBoxesInfo
            if pHostBoxesInfo.boxesnum > 0:
                sdk.lyn_record_event(post_stream, post_event)
                sdk.lyn_stream_wait_event(osd_stream, post_event)

                pHostBoxesInfo_np = np.frombuffer(
                    pHostBoxesInfo,
                    dtype=np.uint8,
                    count=ctypes.sizeof(Box) // np.dtype(np.uint8).itemsize,
                )
                pHostBoxesInfo_ptr = sdk.lyn_numpy_to_ptr(pHostBoxesInfo_np)
                pDevBoxesInfo, ret = sdk.lyn_malloc(ctypes.sizeof(Box))
                common.error_check(
                    sdk.lyn_memcpy_async(
                        osd_stream,
                        pDevBoxesInfo,
                        pHostBoxesInfo_ptr,
                        ctypes.sizeof(Box),
                        sdk.lyn_memcpy_dir_t.ClientToServer,
                    )
                )

                pDevBoxesInfo_ptr = pythonapi.PyCapsule_GetPointer(pDevBoxesInfo, None)
                decodeImg_data_ptr = pythonapi.PyCapsule_GetPointer(
                    cb_data.frame.data, None
                )
                osd_para = struct.pack(
                    "1P3i1P4i",
                    pDevBoxesInfo_ptr,
                    self.vdec_out_info.width,
                    self.vdec_out_info.height,
                    sdk.lyn_pixel_format_t.LYN_PIX_FMT_NV12,
                    decodeImg_data_ptr,
                    24,
                    4,
                    2,
                    4,
                )
                common.error_check(
                    sdk.lyn_plugin_run_async(
                        osd_stream,
                        self.osd_plugin,
                        "lynDrawBoxAndText",
                        osd_para,
                        len(osd_para),
                    )
                )
                common.error_check(
                    sdk.lyn_stream_add_callback(
                        osd_stream,
                        lyn_free_callback,
                        [pDevDetectInfo, pDevBoxesInfo],
                    )
                )

            sdk.lyn_stream_add_callback(
                osd_stream, put_to_queue, [self.__detect_result_queue, cb_data]
            )

            common.print_frame_rate(
                f"{self.device_id}_{self.channel_id}", self.cost_info
            )
            if cancel_flag.value:
                break

        sdk.lyn_synchronize_stream(ipe_stream)
        sdk.lyn_synchronize_stream(apu_stream)
        sdk.lyn_synchronize_stream(post_stream)
        sdk.lyn_synchronize_stream(osd_stream)
        sdk.lyn_destroy_event(ipe_event)
        sdk.lyn_destroy_event(apu_event)
        sdk.lyn_destroy_event(post_event)
        sdk.lyn_destroy_stream(ipe_stream)
        sdk.lyn_destroy_stream(apu_stream)
        sdk.lyn_destroy_stream(post_stream)
        sdk.lyn_destroy_stream(osd_stream)
        sdk.lyn_unload_model(face_detect_model)

        sdk.lyn_destroy_event(ipe_event_begin)
        sdk.lyn_destroy_event(ipe_event_end)
        sdk.lyn_destroy_event(apu_event_begin)
        sdk.lyn_destroy_event(apu_event_end)
        sdk.lyn_destroy_event(post_event_begin)
        sdk.lyn_destroy_event(post_event_end)

        # sdk.lyn_free(pDevDetectInfo)

    def generate_result(self, cancel_flag):
        """存储结果或者用opencv显示"""
        sdk.lyn_set_current_context(self.__ctx)
        if self.show_type == 1:
            encoder = Encoder(
                self.__ctx, self.frame_pool, self.vdec_out_info, self.output_path
            )
        elif self.show_type == 0:
            opencv_window = OpencvWindow(
                self.frame_pool, self.video_frame, self.vdec_out_info
            )
        eos = False
        while not eos:
            cb_data = self.__detect_result_queue.take()
            frame = cb_data.frame
            if cb_data.reconnect:
                self.__detect_result_queue.clear()
                break
            eos = frame.eos
            if self.show_type == 0:
                opencv_window.show(frame)
            elif self.show_type == 1:
                encoder.encode(frame)
            else:
                # 保存json结果
                dump_box_json(self.output_path, cb_data.host_box_info)
                del cb_data.host_box_info
                self.frame_pool.push(frame.data)
            if cancel_flag.value:
                return
        if self.show_type == 1:
            del encoder

    def close(self):
        if self.__decode_send_thread.is_alive():
            self.__decode_send_thread.join()
        if self.__decode_recv_thread.is_alive():
            self.__decode_recv_thread.join()
        if self.__detect_thread.is_alive():
            self.__detect_thread.join()
        if self.__generate_result_thread.is_alive():
            self.__generate_result_thread.join()
        sdk.lyn_plugin_unregister(self.post_plugin)
        sdk.lyn_plugin_unregister(self.osd_plugin)
        self.frame_pool.free_buffers()
        sdk.lyn_vdec_close(self.__vdec_hdl)
        sdk.lyn_demux_close(self.__demux_hdl)
        sdk.lyn_destroy_context(self.__ctx)
